1. Evolution of Operating Systems

第一遍重写进行中

Intro: Computing Systems and Operating Systems


回顾 1. Introduction to Operating Systems,我们知道了操作系统作为一种系统软件,负责封装底层硬件的复杂细节,为用户提供简洁高效的交互方式。但这是站在今天的视角对现代操作系统从上至下的一种审视。在计算机系统发展的过程中,这些抽象是一步一步建立的,并不是一蹴而就的。

操作系统的革新不单单体现在人机交互模式的逐步改进(Bare machine -> CLI -> GUI-> Touch Interface),也受到半导体技术发展和硬件种类增加的驱动(mainframe -> minicomputer -> PC -> handheld)。这些硬件的演进不仅带来了计算能力的提升,也为操作系统设计开辟了新的方向。

在本阶段,我们将结合不同时代的计算机系统,深入探讨那些具有深远影响力的计算机系统和操作系统。每个时代都具有许多富有创造力的想法和技术突破。也正是这些伟大的创新推动了计算机科学的发展,它们既可能引领硬件的诞生与革新,也可能促成操作系统的变迁与进步。

通过这些历史阶段的梳理,我们将分析硬件与操作系统如何相辅相成:硬件的进步为操作系统提供了发挥作用的平台,而操作系统的变迁反过来推动了硬件的广泛应用与普及。本阶段,我们也将讨论操作系统和硬件如何不断创新并解决新的技术挑战,共同塑造现代的计算机系统。

OS Feature Migration

回顾计算机系统的发展历程,从最初动辄占地 30 平米的真空管计算机,到如今比鸡蛋还小的智能手表,我们不难发现计算设备不仅趋于小型化,同时交互方式也逐渐智能化。这种变化主要得益于半导体工艺的突破和操作系统功能的丰富。

根据设备的体积和功能特点,我们可以将计算机的发展阶段划分为以下几类:大型机 (mainframes)小型机 (minicomputers)台式机 (desktop computers)掌上电脑 (handheld computers)智能设备 (smart devices)

随着制造工艺的不断进步,计算机的体积与性能之间的关系发生了变化。在早期,由于技术限制,机器体积越大,性能往往越强。但在半导体工艺提升后,小型化芯片能够承载更高的计算能力,这为操作系统功能的迁移提供了支持。

许多操作系统的功能最初设计用于大型机,然后随着半导体技术的发展才逐步下放至体积更小的设备(同体积晶片能实现更强大的计算性能),实现了更广泛的应用场景。下图展示了不同时期操作系统功能从大型机向小型设备迁移的趋势。

migration_of_OS_concepts_and_features.png

在本阶段的后续课程中,我们以每 10 年作为一个时期来介绍这个时代的计算机系统以及其提供的系统级服务,即支持怎么样的操作系统。

第一课 Early Computing Systems (1940s)


在 1940 年代之前,通用的存储程序的 von Neumann 架构的计算机还未诞生。这时的计算机被称为专用计算机,因为它们只能完成一些特定任务,通常依赖齿轮等机械结构来完成计算。

在 1945 年,John von Neumann 提出了现代计算机的基本架构,即“存储程序”思想。强调将程序和数据放在存储器中,使得计算机可用通过读取存储器中的指令来自动执行任务,而无需人工干预。为后来通用计算机的出现奠定了理论基础。

在第二次世界大战时期,军事上复杂计算的需求也直接地推动了计算机科学的发展。无论是世界上第一台通用的电子计算机——ENIAC,还是最早的 von Neumann 架构的计算机——EDVAC,最早都是用于计算炮弹弹道和研发核武器提供支持(虽然没它们有直接用在二战中)。

1.1 Mainframes in 1940s

在计算机系统发展的早期阶段(1940s),尽管 von Neumann 架构的通用计算机得到了成功运行,但这个时期的计算机都有一些共同点:

  1. 操作原始:使用开关、插拔板 (plugboards) 和打孔纸带 (punch-cards) 与计算机交互。
  2. 体积庞大:使用上万个真空管、电阻和电容,使得计算机体积庞大,能耗极高。
  3. 成本高昂:由于真空管、精密机械部件和复杂的设计,使得制造成本高昂。
  4. 可靠性低:真空管的故障率高,极易损坏。由于线路裸漏,一个虫子 (bug) 就可能导致短路。
  5. 编程复杂:程序员需要使用机器语言编程,通过物理插拔版连接逻辑单元。
  6. 运行较慢:低可靠性和人工操作方式使得计算机大部分时间都处于空闲状态。
  7. 用途单一:仍主要用于军事研究和科学计算。

由于这个时期的计算机通常体积庞大,因此也被称为大型机 (mainframes) ,大型机这个名称之后被沿用了下来。直到今天,我们都会将体积庞大的计算机称为大型机 ,但不同的是,受限于工艺,那个年代将计算机做得像房子一样大是迫不得已。

mainframe_computer.jpg

因为没有操作系统,所以用户需要直接操作硬件。通过控制台 (console) 上的指示灯、按钮和电缆插孔来调试操作机器。我们把这种人机交互方式称为手工操作方式。在这个时期,人们用纸带来存储输出结果和程序代码,避免代码的重复输入。

1.2 Too Slow

虽然使用真空管的计算机用现代的眼光来看非常慢(100 kHz),但仍然比人类计算快多了。但我们仍然有一个问题:早期的计算机系统一次性只能服务一个任务,每次任务计算完成后都需要物理重构硬件(重新在控制台设置开关、重连电缆),这可能耗费数小时甚至数天。

由于计算机在每次任务完成后都必须重新调整硬件,所以即使 CPU 的运算速度远快于人工计算(每秒可执行约 5000 次加法),但大量时间仍被手工操作占用,导致计算机的空闲率非常非常高,真正用于计算的时间极为有限。

这一低效的工作模式直接催生了 50 年代批处理系统的出现,使计算机的利用率得以显著提升。

第二课 Batch Processing Era (1950s - 1960s)


40 年代,大多数计算机系统仍然依赖人工切换任务,效率极低。而且每次计算完成后都需要人工调整硬件,导致计算机的空闲时间远远多于实际计算时间。人们希望计算机每次任务完成后都能够自动地重置状态并加载下一任务,而不依赖人工干预。这一思想最终推动了批处理系统的出现。

50 - 60 年代是批处理的时代。50 年代的单道批处理(如 IBM 的 IBSYS)的出现减少了 CPU 的空闲时间,一定程度上提升了 CPU 的利用率。而至 60 年代,多道批处理 (Multiprogramming Batch Processing) 的多任务调度使得系统能够同时容纳调度多个程序,CPU 利用率一度提升到 50% - 60%(如 IBM OS/360)。

尽管晶体管技术在 1947 年已被发明,但由于 1950 年代尚未实现大规模商用化,计算机仍然依赖真空管进行计算。这就导致 1950 年代的计算机仍然处于真空管计算机的第一代计算机,体积庞大、功耗高且故障率高,因此这个时期仍然是大型机主导的时代。

1960 年代,晶体管与 SLT 混合集成电路(如 IBM System/360)的普及标志着第二代计算机的成熟。随着硬件成本下降和晶体管的广泛应用,计算机的体积得以进一步缩小,同时性能和可靠性得到大幅提升。在这一时期,小型机(Minicomputers) 开始出现,例如 1965 年出现的 DEC PDP-8,它比传统大型机更小(冰箱大小)、更经济(1.8万美元),推动了计算机的普及。

2.1 Mainframes in 1950s

到了 1950 年代,计算机开始不再局限于军事用途,但普通人仍然是接触不到的,这个时期的计算机仍非常昂贵,其受众是军队、大型公司和大型的科研单位。在之后的很长一段时间内(直到1970 年代),你都需要将你自己想运行的程序提交给大型机的操作员代为处理(高校场景)。

2.1.1 IBM 701

相比较于 40 年代手工插拔电缆复位的方式,50 年代的大型机开始支持用户通过控制台直接操作计算机,极大地提升了使用的便利性。比如 1952 年推出的 IBM 701,操作员可以直接在其控制台上输入命令来控制计算机的运行,不需要重新布线。

下面是一张当年的高端计算机 IBM 701 的控制台照片。

IBM_701_console.jpg

现代计算机的处理器、内存等大多都集成再一块主板上。但在 50 年代的计算机上,由于仍然使用电子管,所以和 1940 年代计算机一样,是由几大部分构成的。以 IBM 701 为例,硬件单元就有:

  • IBM 701:计算控制单元,也就是CPU。
  • IBM 706:使用 Williams Tube 的经典存储单元,是系统的内存。
  • IBM 711:打孔卡阅读器,用于数据输入,每分钟可以阅读 150 张打孔卡片。
  • IBM 716:打印机。
  • IBM 721:打孔卡记录机,用于输出计算机处理好的数据,每分钟可以记录 100 张卡片。
  • IBM 726/727:磁带机,用于输入/输出磁带存储数据。
  • IBM 731:磁鼓存储器,用于辅助存储数据。
  • IBM 736/741:电源框架,提供计算机运行所需的电力。
  • IBM 740:阴极射线管输出记录器,用于显示计算结果。
  • IBM 746:电源分配单元,管理整个系统的电力分配。
  • IBM 753:磁带控制单元,可控制 最多 10 台 IBM 727 磁带机。

在 1953 年, John Backus 为 IBM 701 开发了 Speedcoding 的工具,支持通过打孔卡片/磁带连续地加载任务队列。这种能够连续加载多个程序的功能是批处理的雏形,但仍不属于自动批处理系统,因为每次任务结束后仍需要依赖人工调度,手动启停。

2.1.2 IBM 704

1954 年,IBM 推出了 IBM 704,这是首个支持磁芯存储和浮点运算的计算机。如此强悍的性能,加上 Speedcoding 的经验直接促成了首个被广泛应用的高级语言——FORTRAN 的诞生。高级语言的发展也催生了后面 FORTRAN Monitor System,为首个批处理系统 IBSYS (IBM Batch System) 奠定了基础。

IBM_704.jpg

在 1954 年,假如我在使用一台 IBM 704,当我想运行一段 FORTRAN 代码时,我就需要完成如下的步骤:

  1. 加载 FORTRAN 编译器磁带
  2. 运行编译器,将高级语言源程序变为汇编
  3. 卸载 FORTRAN 编译器磁带
  4. 加载汇编磁带
  5. 运行汇编器,将汇编语言源程序转换为机器语言的二进制目标程序
  6. 加载目标程序
  7. 运行目标程序

每一步都需要操作员加载、运行、卸载程序。每当操作员在命令台上操作时,CPU 就会空闲。而 IBM 704 每月租金大约 15000 美元,为了让投资有意义一点,就需要提高 CPU 的利用率,不让 CPU 空闲下来,我们就得让机器自己尽可能不停的加载运行程序。

Batch OS

为了减少机器的空闲时间,人们开发出了自动作业排序 (Automatic Job Sequencing),它不需要人工干预,会自动组织和执行多个任务,从而提高计算机的利用率。这种思想创造了早期的操作系统——IBSYS 批处理系统的出现。

批处理系统最大的好处就是减少了人工操作的时间。假设我有两个 FORTRAN 程序、一个 COBOL 程序,我希望在 IBM 704 上编译它们为汇编代码。我会先将这些程序按类型分成两个批次,以免重复加载程序。在没有批处理系统的情况下,我必须逐步完成以下过程:

  1. 人工加载 FORTRAN 编译器,编译这两个 FORTRAN 程序后卸载。
  2. 人工加载 COBOL 编译器,编译一个 COBAL 程序后卸载。

而有了批处理系统,相同类型的任务可以按批次自动执行,一批次执行完毕后机器自动执行二批次任务。由于这时仍需手动提交打孔卡或磁带,所以操作员需要提交任务前把相同类型的任务整理在一起再提交给批处理系统,以减少相同程序的重复加载:

  • 人按一定格式提交打孔卡或磁带,之后机器自动调度任务。
  • 机器自动加载 FORTRAN 编译器,所有 FORTRAN 代码在同一批次中编译。
  • 卸载 FORTRAN 编译器并自动加载 COBOL 编译器。
  • 所有 COBOL 代码在同一批次中编译,同样机器自动处理整个流程;

计算机自动调度任务,操作员无需手动干预每个步骤。

Resident Monitor

其实 IBM 704 本身并不支持批处理,后期 IBM 704 引入了 IBSYS,才使其能够执行批处理任务。即多个作业可以预先存入打孔卡/磁带中,计算机自动按顺序的执行程序;同时操作员无需手动干预,计算机自动地调度作业。

早期批处理系统仅仅是一段常驻内存的小型程序,称为监控程序 (Monitor),大概只有 4KB 大小。最早的时候,每次机器开机后都要人工地加载 IBSYS 这样的监控程序永驻到内存上。如果你提交了一批任务,监控程序就会帮你逐个地加载并执行任务。当一个任务执行完毕,CPU 控制权就会重新回到监控程序上并调度下一个任务在 CPU 上运行。

常驻的监控程序通常由三部分组成:

  1. 控制卡解释器:决定系统应该加载哪个程序,例如 FORTRAN 编译器或汇编器。
  2. 加载器:负责将程序或编译器加载到内存中,使其可以运行。
  3. 设备驱动器:负责与输入/输出设备(如打孔卡机、磁带机、打印机)进行交互。使计算机能够自动地读取用户提交的数据并存储计算结果。节省了操作员手工操作的时间。

resident_monitor.png

通过引入控制卡 (Control cards),监控程序就能够知道需要运行什么程序。以下图为例,我们有这几类控制卡片:

  • $JOB:标记新作业的开始,通常包含作业的会计信息。
  • $LOAD:指示系统加载程序或编译器到内存中。
  • $END:标记当前作业的结束,用于清理资源。
  • $FTN:加载 FORTRAN 编译器,用于编译 FORTRAN 源代码。
  • $ASM:加载汇编器,将汇编代码转换为机器代码。
  • $RUN:执行已编译的二进制目标程序。

batch_control_cards.png

许多系统会使用 $ 作为控制卡的标识。IBM 的的作业控制语言 (JCL) 使用 // 作为控制卡的标识。

Faster but Slow

即使请来当时世界上最优秀的操作员来操作计算机,人类仍然是要远慢于计算机的运算速度。所以当时的人们开发出批处理软件程序来让计算机自动地执行任务,期间不需要人类干预。这大大地提升了 CPU 的使用率。

然而,即便如此,CPU 仍然面临瓶颈。其主要原因在于机械 I/O 设备的速度限制。在这一时期,计算机每秒已经能够执行数千条指令,但最快的打孔卡阅读机 仍然只能以每分钟几十到几百张卡的速度读取数据,这使得 I/O 成为计算的主要瓶颈。CPU 的利用率可能仍然不到 10% 。

一个解决方案是研发更加快速的 I/O 。这个时代的人们但是短短几年后,晶体管的时代就全面的到来了,CPU 性能增加的速度甚至要快于 I/O 性能增加的速度,差距越来越大。

Overlapped IO

当时一个流行的解决方法就是在读卡器和 CPU 之间加入一层更快速的 IO 设备——磁带机。操作员使用专用设备(如IBM 729磁带机)将卡片批量转存到磁带,此过程是离线拷贝的,不占用主机资源。之后批处理监控程序全速读取磁带数据在 CPU 上面执行,同时另一台磁带机可以执行下一批任务的离线拷贝工作。(类似于 DMA,与 CPU 并行工作)

offline_io.png

通过配合使用多个磁带机,我们就已经可以让 CPU 处于一个还不错的忙碌状态了。但是如果作业运行的过程中需要使用 I/O 就会造成 CPU 的空闲。

2.2 Mainframes and Minicomputers in 1960s

2.2.1 Atlas Operating System

Multiprogramming Batch Processing

Iconic consoles of the IBM System/360 mainframes, 55 years old
批处理系统(Batch Operating System,BOS) 将CPU的利用率提升了一大截,用户可以将要执行的任务一次性全部打包交给机器。之后,用户不需直接操作计算机硬件就能提交作业。可以将BOS视作一位管理员,它负责接收所有用户提交的作业。BOS会将需求相似的作业分组成批次,然后批量发送到计算机上执行。作业完成后,BOS负责将结果返回给用户。

OS/360 是1964年IBM为其当时全新的System/360大型计算机开发的批处理操作系统它标志着企业级计算和批处理操作系统的早期发展。OS/360的直接后续版本包括System/370的操作系统,然后是MVS(Multiple Virtual Storage),最后发展为现代的 z/OS。(最早的指令集架构思想)

  • IBM650(1954):使用磁鼓来存储程序和数据,但用户仍然需要通过物理介质(如punchcard)来输入程序和数据。

第三课 Time-Sharing and PC


在BPOS中,机器允许多个用户同时将任务(jobs)加载进任务队列(job queue)中,这时,系统把需求相似的任务分成不同批次(batch)然后按顺序送入CPU中运行。在任务执行过程中,任务不仅仅需要使用CPU,还会访问磁盘或外设。在任务访问磁盘或I/O时,CPU会处于空闲(idle)状态。这样会无故浪费很多CPU资源。

试想,如果允许内存加载多个作业,我们可以通过特定策略来提高资源的利用率。这就是多道程序设计的由来。在BPOS中,作业需要一个一个按顺序完成。但是Multiprogramming OS允许同时存在多个作业处于活动状态。当一个任务等待I/O时,操作系统可以调度另一个任务使用CPU。这样,即使某些任务在等待I/O操作,CPU也能保持忙碌状态,从而大大提高了效率和吞吐量。
Pasted image 20240912161341.png
多道程序系统其实就是容许多”道“程序同时存在于内存中,通过调度算法来使CPU尽量忙碌,提高CPU的利用率。在多道程序系统中,我们需要理解下面的概念:

  • “道数”:内存中存放作业的数量。
  • 技术支撑:中断、通道

优点

  1. 资源利用率更高
  2. 响应时间变短
    缺点
  3. 复杂性的提升

第四课 Multitasking


Multi-tasking is a logical extension of multiprogramming.


进程隔离的概念这时候开始出现?避免进程之间的信息泄露。而且确保在一个进程crash时其他进程仍然可以继续运行

最早的进程隔离出现在 1962 的 Atlas 超级计算机上

多道程序设计是多任务系统的前身,多道程序设计解决了CPU利用率低下的问题,但毕竟人类需要直接与计算机交互。从而促使了多任务系统的诞生。

多任务操作系统是一种能够“同时”处理多个任务的操作系统。这种类型的操作系统通过高效地分配CPU时间和其他资源,使得用户几乎可以同时运行多个任务,这些任务共享共同的CPU、内存等资源。极大的提高了系统的响应时间和用户体验。

Pasted image 20240912162841.png

Multitasking OS 也叫 time sharing systems (分时系统)。这种操作系统将CPU时间分成很小的片段,称为时间片(time slices)时间量(quantum),并将这些时间片轮流分配给各个任务,从而实现多任务的并发执行 。这种方法的关键在于时间片的长度通常非常短(通常是毫秒级别),足够短到让用户无法感觉到任务之间的切换。多个任务或多个用户感觉都独占CPU和内存,而实际上他们是在分享CPU和内存。

公认的第一个分时操作系统是 1961 年 MIT 开发的CTSS(Compatible Time-Sharing System)。CTSS的开发标志着计算机技术的一个重要转折点,使得多个用户能够通过各自的终端同时使用同一台计。算机资源。这一概念彻底改变了计算机的使用方式,为后来的操作系统,如Multics(多路信息计算服务),以及今天我们使用的现代多任务操作系统奠定了基础。

应当引入并发的概念。。。

1.8 Concurrency and Thread

1.6.1.2 Different Concurrency

在现实中,我们有三种不同类型的并发:进程的并发线程的并发I/O复用。前两者是最好理解的,我们比较了进程和线程的优劣,对于进程的并发来说,每个进程都有自己独立的虚拟空间和资源,隔离性高,可以提供更好的安全性和稳定性;而进程切换的开销使得进程并发可能会影响系统资源。

线程的并发则减少了进程切换带来的系统开销,线程切换时仅仅保留执行时所需要的少量资源,其他的资源都由进程进行管理。而且线程减少了进程间通信的使用。然而,线程之间互相影响增加了并发控制的复杂性,稳定性相对差一点。

I/O复用我们将在学习完I/O系统后单独开一个阶段学习。I/O复用指的是多个I/O操作”同时“被单个进程/线程”同时“所处理,而不需要为每个I/O操作创建一个独立的进程/线程。常见的I/O复用技术有select、poll和epoll

tcp1981
TCP最早是包含IP头字段的

4004 -
1972 - intel releases the first 8-bits microprocessor-8008 - successor - 8080 - 1974
真空管(提供0/1的表示) - ENIAC 巨大的根本原因(真空管还会大量产热,这也是为什么早期计算机功耗如此之高且吸引虫子Bug的原因)

自从真空管发明出以来(30余年),变化很小(附图)

第一个晶体管(solid-state transistors)1940s于at&t bell labs

transistor scale: 找不到“Intel Technology - Architecture All Access Transistor Technology Intel Technology [_PELtLdh87Y - 517x273 - 7m03s].png”。

moore's law - doubling performance while reducing the power comsuption
1995 ASCI Red - 1Tflop performance need 10,000 microprocessors and 500kW
of power
2005, just 10 years later, Intel XEON PHI 1Tflop with 80 CPUs (signle conputer motherboard and with 67W